package sk.naive.talker.command;

import sk.naive.talker.*;
import sk.naive.talker.message.DefaultMessageFactory;
import sk.naive.talker.props.PropertyStoreException;
import sk.naive.talker.persistence.*;

import sk.naive.talker.util.Utils;

import java.rmi.RemoteException;
import java.util.regex.*;
import java.util.logging.Level;
import java.util.*;

/**
 *
 * @author <a href="mailto:rytier@naive.deepblue.sk">Martin "Rytier" Kerni</a>
 * @version $Revision: 1.29 $ $Date: 2005/03/15 19:59:58 $
 */
public class RoomAdmin extends AbstractCommand {
	private static final String LCMD_HIDE = "hide";
	private static final String LCMD_OWNER = "owner";
	private static final String LCMD_SHOW = "show";
	private static final String LCMD_LINK_ADD = "ladd";
	private static final String LCMD_LINK_REMOVE = "lremove";
	private static final String LPARAM_FROM = "from";
	private static final String LPARAM_TO = "to";

	public static final String PERM_ROOM_ADMIN = "roomAdmin";

	public void exec()
		throws CommandException,
		RemoteException
	{

		if (params == null) {
			sendMisusageWarning();
			return;
		}
		Pattern pattern = Pattern.compile("(\\S+) +(\\S+)(?: +(?:(from|to) +)?(\\S+))?");
		Matcher matcher = pattern.matcher(params);
		if (!matcher.matches()) {
			sendMisusageWarning();
			return;
		}

		LocationFinder lf = commandDispatcher.locationFinder();

		String roomName = matcher.group(1);
		String roomCmd = matcher.group(2).toLowerCase();

		checkPermissions(roomCmd, roomName, lf);

		try {
			if (CommandKeywords.CREATE.startsWith(roomCmd)) {
				Location loc = lf.createLocation(roomName);
				if (loc != null) {
					ctxSet(DefaultMessageFactory.CTXKEY_VAL, loc.getName());
					sendHelper().sendMessage(user, "roomAdmin.created");
					logger().log(Level.INFO, "User {0} created room {1}.", new Object[] {user.getLogin(), loc.getName()});
				} else {
					ctxSet(DefaultMessageFactory.CTXKEY_VAL, roomName);
					sendHelper().sendMessage(user, "roomAdmin.alreadyExists");
				}
			} else if (CommandKeywords.REMOVE.startsWith(roomCmd)) {
				Location loc = lf.findLocationByName(roomName);
				if (loc == null) {
					noSuchRoomWarning(user, roomName);
					return;
				}
				lf.destroy(loc);
				ctxSet(DefaultMessageFactory.CTXKEY_VAL, loc.getName());
				sendHelper().sendMessage(user, "roomAdmin.removed");
				logger().log(Level.INFO, "User {0} removed room {1}.", new Object[] {user.getLogin(), loc.getName()});
			} else if (CommandKeywords.RENAME.startsWith(roomCmd)) {
				if (matcher.group(4) == null || matcher.group(3) != null) {
					sendMisusageWarning();
					return;
				}
				Location loc = lf.findLocationByName(roomName);
				if (loc == null) {
					noSuchRoomWarning(user, roomName);
					return;
				}
				String oldName = loc.getName();
				String newName = Utils.normalize(matcher.group(4));
				lf.rename(loc, newName);
				ctxSet(DefaultMessageFactory.CTXKEY_VAL, oldName);
				ctxSet(DefaultMessageFactory.CTXKEY_VAL + 1, loc.getName());
				sendHelper().sendMessage(user, "roomAdmin.renamed");
				logger().log(Level.INFO, "User {0} renamed room {1} to {2}.", new Object[] {user.getLogin(), oldName, newName});
			} else if (LCMD_OWNER.startsWith(roomCmd)) {
				if (matcher.group(3) != null) {
					sendMisusageWarning();
					return;
				}
				Location loc = lf.findLocationByName(roomName);
				if (loc == null) {
					noSuchRoomWarning(user, roomName);
					return;
				}
				if (matcher.group(4) == null) {
					ctxSet(DefaultMessageFactory.CTXKEY_VAL, ownerListFormatted(loc));
					ctxSet(DefaultMessageFactory.CTXKEY_VAL + 1, loc.getName());
					if (loc.getLocationOwners().size() <= 1) {
						sendHelper().sendMessage(user, "roomAdmin.owner");
					} else {
						sendHelper().sendMessage(user, "roomAdmin.ownerList");
					}
					return;
				}
				User owner = commandDispatcher.getUserFinder().findUserByLogin(matcher.group(4));
				if (owner == null) {
					sendUserNotFoundMessage(matcher.group(4));
					return;
				}
				ctxSet(DefaultMessageFactory.CTXKEY_VAL, owner.getProperties());
				ctxSet(DefaultMessageFactory.CTXKEY_VAL + 1, loc.getName());
				if (loc.getLocationOwners().contains(owner.getId())) {
					loc.removeOwner(owner.getId());
					sendHelper().sendMessage(user, "roomAdmin.ownerRemoved");
					logger().log(Level.INFO, "User {0} removed owner of room {1} - user {2}.", new Object[] {user.getLogin(), loc.getName(), owner.getLogin()});
				} else {
					loc.addOwner(owner.getId());
					sendHelper().sendMessage(user, "roomAdmin.ownerAdded");
					logger().log(Level.INFO, "User {0} added owner of room {1} - user {2}.", new Object[] {user.getLogin(), loc.getName(), owner.getLogin()});
				}
			} else if (LCMD_SHOW.startsWith(roomCmd)) {
				Location loc = lf.findLocationByName(roomName);
				if (loc == null) {
					noSuchRoomWarning(user, roomName);
					return;
				}
				loc.set(Location.LPROP_VISIBILITY, "true");
				ctxSet(DefaultMessageFactory.CTXKEY_VAL, loc.getName());
				sendHelper().sendMessage(user, "roomAdmin.shown");
			} else if (LCMD_HIDE.startsWith(roomCmd)) {
				Location loc = lf.findLocationByName(roomName);
				if (loc == null) {
					noSuchRoomWarning(user, roomName);
					return;
				}
				loc.set(Location.LPROP_VISIBILITY, "false");
				ctxSet(DefaultMessageFactory.CTXKEY_VAL, loc.getName());
				sendHelper().sendMessage(user, "roomAdmin.hidden");
			} else if (LCMD_LINK_ADD.startsWith(roomCmd)) {
				new LinkOperation() {
					protected void performSpecific(Location from, Location to, User user)
						throws PersistenceException, PropertyStoreException, RemoteException
					{
						from.addExit(to);
						ctxSet(DefaultMessageFactory.CTXKEY_VAL, from.getName());
						ctxSet(DefaultMessageFactory.CTXKEY_VAL + 1, to.getName());
						sendHelper().sendMessage(user, "roomAdmin.linkAdded");
						logger().log(Level.INFO, "User {0} added link from {1} to {2}.", new Object[] {user.getLogin(), from.getName(), to.getName()});
					}
				}.perform(user, roomName, matcher.group(4), matcher.group(3), lf);
			} else if (LCMD_LINK_REMOVE.startsWith(roomCmd)) {
				new LinkOperation() {
					protected void performSpecific(Location from, Location to, User user)
						throws PersistenceException, PropertyStoreException, RemoteException
					{
						from.removeExit(to);
						ctxSet(DefaultMessageFactory.CTXKEY_VAL, from.getName());
						ctxSet(DefaultMessageFactory.CTXKEY_VAL + 1, to.getName());
						sendHelper().sendMessage(user, "roomAdmin.linkRemoved");
						logger().log(Level.INFO, "User {0} removed link from {1} to {2}.", new Object[] {user.getLogin(), from.getName(), to.getName()});
					}
				}.perform(user, roomName, matcher.group(4), matcher.group(3), lf);
			} else if (CommandKeywords.EDIT.startsWith(roomCmd)) {
				Location loc = lf.findLocationByName(roomName);
				if (loc == null) {
					noSuchRoomWarning(user, roomName);
					return;
				}
				ctxSet(DefaultMessageFactory.CTXKEY_VAL, loc.getName());
				new EditorHelper(this, loc).start(loc);
			} else {
				sendMisusageWarning();
			}
		} catch (PersistenceException e) {
			throw new CommandException(e);
		} catch (PropertyStoreException e) {
			throw new CommandException(e);
		}
	}

	private void checkPermissions(String roomCmd, String roomName, LocationFinder lf) throws PermissionException {
		if (CommandKeywords.CREATE.startsWith(roomCmd)) {
			Utils.checkPermission(user, PERM_ROOM_ADMIN);
		}
		if (!CommandKeywords.CREATE.startsWith(roomCmd) && lf.findLocationByName(roomName) != null && !lf.findLocationByName(roomName).getLocationOwners().contains(user.getId())) {
			Utils.checkPermission(user, PERM_ROOM_ADMIN);
		}
	}

	protected String ownerListFormatted(Location loc) throws PropertyStoreException {
		StringBuilder sb = new StringBuilder();
		Set<Integer> ownerIds = (Set<Integer>) loc.get(Location.LPROP_OWNERS);
		Set<String> owners = new HashSet<String>();
		for (Integer ownerId : ownerIds) {
        	owners.add((String) commandDispatcher.getUserFinder().findUserById(ownerId).get(User.UPROP_NAME));
		}
		sb.append(Utils.formatCollection(owners, ", "));

		if (sb.length() == 0) {
			sb.append(getString("message.nobody", user.getProperties()));
		}

		return sb.toString();
	}

	private void noSuchRoomWarning(User u, String roomName) throws RemoteException {
		ctxSet(DefaultMessageFactory.CTXKEY_VAL, roomName);
		sendHelper().sendMessage(u, "roomAdmin.noSuchRoom");
	}

	private abstract class LinkOperation {
		public void perform(User u, String fromName, String toName, String dirSpec, LocationFinder lf)
			throws PersistenceException, PropertyStoreException, RemoteException
		{
			Location from = lf.findLocationByName(fromName);
			Location to = lf.findLocationByName(toName);
			if (from == null) {
				noSuchRoomWarning(u, fromName);
				return;
			}
			if (to == null) {
				noSuchRoomWarning(u, toName);
				return;
			}
			if (dirSpec == null || dirSpec.equals(LPARAM_FROM)) {
				performSpecific(to, from, u);
			}
			if (dirSpec == null || dirSpec.equals(LPARAM_TO)) {
				performSpecific(from, to, u);
			}
		}

		protected abstract void performSpecific(Location from, Location to, User user)
			throws PersistenceException, PropertyStoreException, RemoteException;
	}

	private class EditorHelper extends AbstractEditorHelper {
		private Location loc;

		public EditorHelper(AbstractCommand comm, Location loc) {
			super(RoomAdmin.this.user, comm);
			this.loc=loc;
		}

		protected Text getOriginalText(PersistentObject object, TextPersistence db) throws PersistenceException {
			List<Text> texts = db.loadTexts(object.getId(), textType(), "");
			if (texts.isEmpty()) {
				return null;
			}
			return texts.get(0);
		}

		protected int textType() {
			return TextRelation.LOCATION_DESC;
		}

		protected void sendNotify() throws RemoteException {
			ctxSet(DefaultMessageFactory.CTXKEY_VAL, loc.getName());
			super.sendNotify();
		}

		protected String getUserKeyBegin() {
			 return "roomAdmin.descStart";
		 }

		protected String getUserKeySend() {
			 return "roomAdmin.descSuccess";
		 }

		protected String getUserKeyCancel() {
			 return "roomAdmin.descCancel";
		}

		protected Text createNewText(TextPersistence tp, String s) throws PersistenceException {
			return new Text(tp, s);
		}
	}
}